<?php
	
	class RestRequest
	{
		/** Object Specific, Non-Modifiable Variables **/
		protected $url;
		protected $request_body;
		protected $request_length;
		/** Variables available to be modified through functionality **/
		protected $verb;
		protected $accept_type;
		protected $username;
		protected $password;
		/** Dynamically generated variables, based from executions **/
		protected $response_body;
		protected $response_info;
		
		/**
		 * Construct
		 * Sets all REST information regarding the request
		 */
		public function __construct($url = NULL, $verb = 'GET', $request_body = NULL)
		{
			$this->url				= $url;
			$this->verb				= $verb;
			$this->request_body		= $request_body;
			$this->request_length	= 0;
			$this->username			= NULL;
			$this->password			= NULL;
			$this->accept_type		= 'application/json';
			$this->response_body	= NULL;
			$this->response_info	= NULL;
			
			if($this->request_body !== NULL)
			{
				$this->build_post_body();
			}
		}
		
		/**
		 * Flushes request data, so another request may be 
		 * made to the same REST
		 */
		public function flush()
		{
			$this->request_body		= NULL;
			$this->request_length	= 0;
			$this->verb				= 'GET';
			$this->response_body	= NULL;
			$this->response_info	= NULL;
		}
		
		/**
		 * Begins the REST request sequence by applying all
		 * neccessary functionality
		 */
		public function execute()
		{
			$curl = curl_init();
			$this->set_auth($curl);
			
			try
			{
				switch(strtoupper($this->verb))
				{
					case 'GET':
						$this->execute_GET($curl);
						break;
					case 'POST':
						$this->execute_POST($curl);
						break;
					case 'PUT':
						$this->execute_PUT($curl);
						break;
					case 'DELETE':
						$this->execute_DELETE($curl);
						break;
					default:
						throw new InvalidArgumentException('Current verb (' . $this->verb . ') is an invalid REST verb.');
				}
			}
			catch(InvalidArgumentException $e)
			{
				curl_close($curl);
				throw $e;
			}
			catch(Exception $e)
			{
				curl_close($curl);
				throw $e;
			}
		}
		
		/**
		 * urlencodes POST data into the request body
		 */
		public function build_post_body($data = NULL)
		{
			$data = ($data !== NULL) ? $data : $this->request_body;
			
			if(!is_array($data))
			{
				throw new InvalidArgumentException('Invalid data input for postBody');
			}
			
			$newdata = false;
			
			foreach($data as $key => $value)
			{
				$newdata .= $key . '=' . urlencode($value) . '&';
			}
			
			$newdata = rtrim($newdata, '&');
						
			$data = http_build_query($data, '', '&');
			$this->request_body = $newdata;
		}
		
		/**
		 * Executes GET
		 */
		protected function execute_GET($curl)
		{
			$this->do_execute($curl);
		}
		
		/**
		 * Executes POST
		 */
		protected function execute_POST($curl)
		{
			if(!is_string($this->request_body))
			{
				$this->build_post_body();
			}
			
			curl_setopt($curl, CURLOPT_POSTFIELDS, $this->request_body);
			curl_setopt($curl, CURLOPT_POST, true);
			
			$this->do_execute($curl);
		}
		
		/**
		 * Executes PUT
		 */
		protected function execute_PUT($curl)
		{
			if(!is_string($this->request_body))
			{
				$this->build_post_body();
			}
			
			$this->request_length = strlen($this->request_body);
			
			$fh = fopen('php://temp', 'rw');
			fwrite($fh, $this->request_body);
			rewind($fh);
			
			curl_setopt($curl, CURLOPT_INFILE, $fh);
			curl_setopt($curl, CURLOPT_INFILESIZE, $this->request_length);
			curl_setopt($curl, CURLOPT_PUT, true);
			
			$this->do_execute($curl);
			
			fclose($fh);
		}
		
		/**
		 * Executes DELETE
		 */
		protected function execute_DELETE($curl)
		{
			curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'DELETE');
			
			$this->do_execute($curl);
		}
		
		/**
		 * Runs the execute, this command should not be called
		 * directly, as it is called from one of the overlaying
		 * execute_COMMAND functions
		 */
		protected function do_execute(&$curl)
		{
			$this->set_curlopts($curl);
			$this->response_body = curl_exec($curl);
			$this->response_info = curl_getinfo($curl);
			
			curl_close($curl);
		}
		
		/**
		 * Sets standard curlopts for the REST request
		 */
		protected function set_curlopts(&$curl)
		{
			curl_setopt($curl, CURLOPT_TIMEOUT, 10);
			curl_setopt($curl, CURLOPT_URL, $this->url);
			curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
			curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
			curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
			curl_setopt($curl, CURLOPT_HTTPHEADER, array('Accept: ' . $this->accept_type));
		}
		
		/**
		 * Called during an execution of a command, if neccessary
		 * uses HTTP authentication to authenticate the request
		 */
		protected function set_auth(&$curl)
		{
			if($this->username != NULL || $this->password != NULL)
			{
				curl_setopt($curl, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST);
				curl_setopt($curl, CURLOPT_USERPWD, $this->username . ':' . $this->password);
			}
		}
		
		/**
		 * Gets or Sets the HTTP authentication username
		 * information for the next REST execution
		 */
		public function username($username = NULL)
		{
			if($username == NULL)
			{
				return($this->username);
			}
			
			return($this->username = $username);
		}
		
		/**
		 * Gets or sets the HTTP authentication password
		 * information for the next REST execution
		 */
		public function password($password = NULL)
		{
			if($password == NULL)
			{
				return($password);
			}
			
			return($this->password = $password);
		}
		
		/**
		 * Gets or sets the verbature for the next
		 * REST execution
		 */
		public function verb($verb = NULL)
		{
			if($verb == NULL)
			{
				return($this->verb);
			}
			
			return($this->verb = $verb);
		}
		
		/**
		 * Gets or sets the accept_type for the
		 * next REST execution
		 */
		public function accept_type($accept_type = NULL)
		{
			if($accept_type == NULL)
			{
				return($this->accept_type);
			}
			
			return($this->accept_type = $accept_type);
		}
		
		/**
		 * Returns the HTTP response code from the previous
		 * executed REST
		 */
		public function response_info()
		{
			if($this->response_info != NULL)
			{
				return((object) $this->response_info);
			}
		}
		
		/**
		 * Returns the HTTP response body from the previous
		 * executed REST
		 */
		public function response()
		{
			if($this->response_body != NULL)
			{
				return($this->response_body);
			}
			
			return(false);
		}
	}
	
?>